/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections;
using ELE.EnterpriseLibrary.Common.Logging;

namespace ELE.EnterpriseLibrary.Naming
{
  /// <summary>
  /// This interface represents a naming context, which consists of a set of name-to-object bindings. 
  /// It contains methods for examining and updating these bindings. 
  /// <list>
  /// <item>
  /// <term><b>Names</b></term>
  /// <description>
  ///  Each name passed as an argument to a Context method is relative to that context. 
  ///  The empty name is used to name the context itself. A name parameter may never be null. 
  ///  Most of the methods have overloaded versions with one taking a Name parameter and one 
  ///  taking a String. These overloaded versions are equivalent in that if the Name and String 
  ///  parameters are just different representations of the same name, then the overloaded versions 
  ///  of the same methods behave the same. In the method descriptions below, only one version is 
  ///  fully documented. The second version instead has a link to the first: the same documentation 
  ///  applies to both. 
  ///
  ///  For systems that support federation, String name arguments to Context methods are composite 
  ///  names. Name arguments that are instances of CompositeName are treated as composite names, 
  ///  while Name arguments that are not instances of CompositeName are treated as compound names 
  ///  (which might be instances of CompoundName or other implementations of compound names). 
  ///  This allows the results of NameParser.parse() to be used as arguments to the Context methods. 
  ///  Prior to ONDI 1.2, all name arguments were treated as composite names. 
  ///
  ///  Furthermore, for systems that support federation, all names returned in a NamingEnumeration 
  ///  from list() and listBindings() are composite names represented as strings. See CompositeName 
  ///  for the string syntax of names. 
  ///
  ///  For systems that do not support federation, the name arguments (in either Name or String forms) 
  ///  and the names returned in NamingEnumeration may be names in their own namespace rather than 
  ///  names in a composite namespace, at the discretion of the service provider.   
  /// </description>
  /// </item>  
  /// <item>
  /// <term><b>Exceptions</b></term>
  /// <description>
  ///  All the methods in this interface can throw a NamingException or any of its subclasses. 
  ///  See NamingException and their subclasses for details on each exception  
  /// </description>
  /// </item>
	/// </list>
  /// </summary>
	public interface INamingContext
	{
    /// <summary>
    /// Retrieves the named object. If name is empty, returns a new instance of this context 
    /// (which represents the same naming context as this context, but its environment may be 
    /// modified independently and it may be accessed concurrently).
    /// </summary>
    /// <param name="pName">the name of the object to look up</param>
    /// <returns>the object bound to name</returns>
    object Lookup(IName pName);
    /// <summary>
    /// Retrieves the named object. If name is empty, returns a new instance of this context 
    /// (which represents the same naming context as this context, but its environment may be 
    /// modified independently and it may be accessed concurrently).
    /// </summary>
    /// <param name="pName">the name of the object to look up</param>
    /// <returns>the object bound to name</returns>
    object Lookup(string pName);
    /// <summary>
    /// Retrieves the named object, following links except for the terminal atomic 
    /// component of the name. If the object bound to name is not a link, returns the 
    /// object itself
    /// </summary>
    /// <param name="name">the name of the object to look up</param>
    /// <returns>the object bound to name, not following the terminal link (if any).</returns>
    object LookupLink(IName name);
    /// <summary>
    /// <see cref="LookupLink(IName)"/>
    /// </summary>
    /// <param name="name">the name of the object to look up</param>
    /// <returns>the object bound to name, not following the terminal link (if any).</returns>
    object LookupLink(string name);
    /// <summary>
    /// Adds a new environment property to the environment of this context. 
    /// If the property already exists, its value is overwritten. See class description for 
    /// more details on environment properties 
    /// </summary>
    /// <param name="propName">the name of the environment property to add; may not be null</param>
    /// <param name="propVal">the value of the property to add; may not be null</param>
    /// <returns>the previous value of the property, or null if the property was not in the 
    /// environment before</returns>
    object AddToEnvironment(string propName, object propVal);
    /// <summary>
    /// Removes an environment property from the environment of this context. 
    /// See class description for more details on environment properties
    /// </summary>
    /// <param name="propName">the name of the environment property to remove; may not be null</param>
    /// <returns>the previous value of the property, or null if the property was not in the 
    /// environment</returns>
    object RemoveFromEnvironment(string propName);
    /// <summary>
    /// Retrieves the environment in effect for this context. See class description for more details on 
    /// environment properties. 
    /// The caller should not make any changes to the object returned: their effect on the context is 
    /// undefined. The environment of this context may be changed using addToEnvironment() and 
    /// removeFromEnvironment().
    /// </summary>
    Hashtable Environment { get; }
    /// <summary>
    /// Retrieves the full name of this context within its own namespace.     
    /// Many naming services have a notion of a "full name" for objects in 
    /// their respective namespaces. For example, an LDAP entry has a distinguished name, 
    /// and a DNS record has a fully qualified name. This method allows the client application 
    /// to retrieve this name. The string returned by this method is not a ONDI composite name 
    /// and should not be passed directly to context methods. In naming systems for which the 
    /// notion of full name does not make sense, OperationNotSupportedException is thrown.
    /// </summary>
    string NameInNamespace { get; }
    /// <summary>
    /// Binds a name to an object. All intermediate contexts and the target context (that named by 
    /// all but terminal atomic component of the name) must already exist.
    /// </summary>
    /// <param name="name">the name to bind; may not be empty</param>
    /// <param name="obj">the object to bind; possibly null</param>
    void Bind(IName name, object obj);
    /// <summary>
    /// Binds a name to an object. All intermediate contexts and the target context (that named by 
    /// all but terminal atomic component of the name) must already exist.
    /// </summary>
    /// <param name="name">the name to bind; may not be empty</param>
    /// <param name="obj">the object to bind; possibly null</param>
    void Bind(string name, object obj);
    /// <summary>
    /// Binds a name to an object, overwriting any existing binding. All intermediate contexts and 
    /// the target context (that named by all but terminal atomic component of the name) must 
    /// already exist. 
    /// If the object is a DirContext, any existing attributes associated with the name are 
    /// replaced with those of the object. Otherwise, any existing attributes associated with the 
    /// name remain unchanged.
    /// </summary>
    /// <param name="name">the name to bind; may not be empty</param>
    /// <param name="obj">the object to bind; possibly null</param>
    void ReBind(IName name, object obj);
    /// <summary>
    /// Binds a name to an object, overwriting any existing binding. All intermediate contexts and 
    /// the target context (that named by all but terminal atomic component of the name) must 
    /// already exist. 
    /// If the object is a DirContext, any existing attributes associated with the name are 
    /// replaced with those of the object. Otherwise, any existing attributes associated with the 
    /// name remain unchanged.
    /// </summary>
    /// <param name="name">the name to bind; may not be empty</param>
    /// <param name="obj">the object to bind; possibly null</param>
    void ReBind(string name, object obj);
    /// <summary>
    /// Unbinds the named object. Removes the terminal atomic name in 
    /// name from the target context--that named by all but the terminal atomic part of name. 
    /// This method is idempotent. It succeeds even if the terminal atomic name is not bound in 
    /// the target context, but throws NameNotFoundException if any of the intermediate contexts 
    /// do not exist. 
    /// Any attributes associated with the name are removed. Intermediate contexts are not changed.
    /// </summary>
    /// <param name="name">the name to unbind; may not be empty</param>
    void UnBind(IName name);
    /// <summary>
    /// <see cref="UnBind(IName)"/>
    /// </summary>
    /// <param name="name">the name to unbind; may not be empty</param>
    void UnBind(string name);
    /// <summary>
    /// Binds a new name to the object bound to an old name, and unbinds the old name. 
    /// Both names are relative to this context. Any attributes associated with the old 
    /// name become associated with the new name. Intermediate contexts of the old name 
    /// are not changed.
    /// </summary>
    /// <param name="oldName">the name of the existing binding; may not be empty</param>
    /// <param name="newName">the name of the new binding; may not be empty</param>
    /// <exception cref="NameAlreadyBoundException"/>
    void Rename(IName oldName, IName newName);
    /// <summary>
    /// <see cref="Rename(IName, IName)"/>
    /// </summary>
    /// <param name="oldName">the name of the existing binding; may not be empty</param>
    /// <param name="newName">the name of the new binding; may not be empty</param>
    /// <exception cref="NameAlreadyBoundException"/>
    void Rename(string oldName, string newName);
    /// <summary>
    /// Closes this context. This method releases this context's resources immediately, instead of 
    /// waiting for them to be released automatically by the garbage collector. 
    /// This method is idempotent: invoking it on a context that has already been closed has no 
    /// effect. Invoking any other method on a closed context is not allowed, and results in 
    /// undefined behaviour.
    /// </summary>
    void Close();
    /// <summary>
    /// Composes the name of this context with a name relative to this context. 
    /// Given a name (name) relative to this context, and the name (prefix) of this context 
    /// relative to one of its ancestors, this method returns the composition of the two names 
    /// using the syntax appropriate for the naming system(s) involved. That is, if name names an 
    /// object relative to this context, the result is the name of the same object, but relative 
    /// to the ancestor context. None of the names may be null. <br/><br/>
    /// For example, if this context is named "wiz.com" relative to the initial context, then 
    /// ComposeName("east", "wiz.com")	might return "east.wiz.com". <br/><br/>
    /// If instead this context is named "org/research" might return "org/research/user/jane" while 
    /// ComposeName("user/jane", "research")	returns "research/user/jane".
    /// </summary>
    /// <param name="name">a name relative to this context</param>
    /// <param name="prefix">the name of this context relative to one of its ancestors</param>
    /// <returns>the composition of prefix and name</returns>
    IName ComposeName(IName name, IName prefix);
    /// <summary>
    /// Composes the name of this context with a name relative to this context. <see cref="ComposeName(IName, IName)"/>  for details.
    /// </summary>
    /// <param name="name">a name relative to this context</param>
    /// <param name="prefix">the name of this context relative to one of its ancestors</param>
    /// <returns>the composition of prefix and name</returns>
    string ComposeName(string name, string prefix);
    /// <summary>
    /// Get a subcontext that lives within this context implementation
    /// </summary>
    /// <param name="name">the name of the context</param>
    /// <returns>The sub-context or null if none found by the given name</returns>
    INamingContext GetSubcontext(IName name);
    /// <summary>
    /// <see cref="ELE.EnterpriseLibrary.Naming.INamingContext.GetSubcontext(IName)"/> for details
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    INamingContext GetSubcontext(string name);
    /// <summary>
    /// Get the default subcontext
    /// </summary>
    /// <returns></returns>
    INamingContext GetSubcontext();
    /// <summary>
    /// Creates and binds a new context. Creates a new context with the given 
    /// name and binds it in the target context (that named by all but terminal 
    /// atomic component of the name). All intermediate contexts and the target 
    /// context must already exist.
    /// </summary>
    /// <param name="name">the name of the context to create; may not be empty</param>
    /// <returns>the newly created context</returns>
    INamingContext CreateSubcontext(IName name);
    /// <summary>
    /// Creates and binds a new context. <see cref="ELE.EnterpriseLibrary.Naming.INamingContext.CreateSubcontext(IName)"/> for details.
    /// </summary>
    /// <param name="name">the name of the context to create; may not be empty</param>
    /// <returns>the newly created context</returns>
    INamingContext CreateSubcontext(string name);
    /// <summary>
    /// Destroys the named context and removes it from the namespace. 
    /// Any attributes associated with the name are also removed. Intermediate contexts are not destroyed.
    /// This method is idempotent. It succeeds even if the terminal atomic name is not bound in the 
    /// target context, but throws NameNotFoundException if any of the intermediate contexts do not exist. 
    /// In a federated naming system, a context from one naming system may be bound to a name in another. 
    /// One can subsequently look up and perform operations on the foreign context using a composite name. 
    /// However, an attempt destroy the context using this composite name will fail with NotContextException, 
    /// because the foreign context is not a "subcontext" of the context in which it is bound. Instead, use 
    /// Unbind() to remove the binding of the foreign context. Destroying the foreign context requires that 
    /// the DestroySubcontext() be performed on a context from the foreign context's "native" naming system.
    /// </summary>
    /// <param name="name">the name of the context to be destroyed; may not be empty</param>
    void DestroySubcontext(IName name);
    /// <summary>
    /// look at DestroySubContext(ELE.EnterpriseLibrary.Naming.IName) for explanation
    /// </summary>
    /// <param name="name">the name of the context to be destroyed; may not be empty</param>
    void DestroySubcontext(string name);
    /// <summary>
    /// Retrieves the parser associated with the named context. 
    /// In a federation of namespaces, different naming systems will parse names differently. 
    /// This method allows an application to get a parser for parsing names into their atomic 
    /// components using the naming convention of a particular naming system. Within any single 
    /// naming system, NameParser objects returned by this method must be equal 
    /// (using the equals() test).
    /// </summary>
    /// <param name="name">the name of the context from which to get the parser</param>
    /// <returns>a name parser that can parse compound names into their atomic components</returns>
    INameParser GetNameParser(IName name);
    /// <summary>
    /// <see cref="GetNameParser(IName)"/>
    /// </summary>
    /// <param name="name">the name of the context from which to get the parser</param>
    /// <returns>a name parser that can parse compound names into their atomic components</returns>
    INameParser GetNameParser(string name);
    /// <summary>
    /// Enumerates the names bound in the named context, along with the class names of 
    /// objects bound to them. The contents of any subcontexts are not included. 
    /// If a binding is added to or removed from this context, its effect on an enumeration 
    /// previously returned is undefined.
    /// </summary>
    /// <param name="name">the name of the context to list</param>
    /// <returns>an enumeration of the names and class names of the bindings in this context. 
    /// Each element of the enumeration is of type NameClassPair</returns>
    IEnumerator List(IName name);
    /// <summary>
    /// <see cref="List(IName)"/>
    /// </summary>
    /// <param name="name">the name of the context to list</param>
    /// <returns>an enumeration of the names and class names of the bindings in this context. 
    /// Each element of the enumeration is of type NameClassPair</returns>
    IEnumerator List(string name);
    /// <summary>
    /// Enumerates the names bound in the named context, along with the objects bound to them. 
    /// The contents of any subcontexts are not included.     
    /// If a binding is added to or removed from this context, its effect on an enumeration 
    /// previously returned is undefined.
    /// </summary>
    /// <param name="name">the name of the context to list</param>
    /// <returns>an enumeration of the bindings in this context. 
    /// Each element of the enumeration is of type Binding</returns>
    IEnumerator ListBindings(IName name);
    /// <summary>
    /// <see cref="ListBindings(IName)"/>
    /// </summary>
    /// <param name="name">the name of the context to list</param>
    /// <returns>an enumeration of the bindings in this context. 
    /// Each element of the enumeration is of type Binding</returns>
    IEnumerator ListBindings(string name);
    /// <summary>
    /// Logs a message to a context wide log if implemented
    /// </summary>
    /// <param name="message"></param>
    /// <param name="exept"></param>
    /// <param name="logLevel"></param>
    void Log(string message, Exception exept, LogLevel logLevel);
  }
}
